
#include "World.h"
#include "../ScriptApi.h"

namespace GTA
{

#pragma region Main
	World::World()
	{
		Peds = gcnew ObjectPool(100);
		currentpedsearchid = 126;
		Vehicles = gcnew ObjectPool(100);
		currentvehiclesearchid = 126;
		Objects = gcnew ObjectPool(1000);
		currentobjectsearchid = 126;

		GetPedsExclusions = gcnew Collections::ArrayList();
		GetVehiclesExclusions = gcnew Collections::ArrayList();
		GetObjectsExclusions = gcnew Collections::ArrayList();

		GTA::Events::RunTick::Handler +=  gcnew System::EventHandler<EventArgs^>(World::RunTick);
		GTA::Events::CleanUp::Handler +=  gcnew System::EventHandler<GTA::Events::CleanUpEventArgs^>(World::CleanUp);
	}
	void World::RunTick(Object^ sender, EventArgs^ args)
	{
		SearchPedsTick();
		SearchVehiclesTick();
		SearchObjectsTick();

		AimTick();
	}

	void World::CleanUp(Object^ sender, GTA::Events::CleanUpEventArgs^ args)
	{

	}

#pragma endregion Constructor, Tick(), CleanUp()

#pragma region Util
	// ---------------- Util ------------

	float World::GetDistance(Vector3^ coords1, Vector3^ coords2)
	{
		float dx = coords1->X-coords2->X;
		float dy = coords1->Y-coords2->Y;
		float dz = coords1->Z-coords2->Z;
		float distance = (float)System::Math::Sqrt(dx*dx + dy*dy + dz*dz);
		return distance;
	}

	// ----------------
#pragma endregion GetDistance()

#pragma region Blip
/*
	
	void World::AddBlip(Blip^ blip)
	{
		if (blip == nullptr) {Log::error("World::AddBlip null"); return;};
		for each (Blip^ bl in Blips)
		{
			if (bl == nullptr) continue;
			if (bl == blip) return; // already in list
		}
		Forms->Add(blip);
	}

	void World::RemoveBlip(Blip^ blip)
	{
		int foundid = -1;
		for (int i=0;i<Blips->Count;i++)
		{
			if (Blips[i] == blip) foundid = i;
		}
		if (foundid >= 0) Blips->Remove(blip); 
	}

	void World::UpdateBlips()
	{
		for each (Blip^ blip in Blips)
		{
			if (blip == nullptr) continue;
			if (!blip->Visible) continue;

		}
	}
	*/
#pragma endregion AddBlip(), RemoveBlip(), UpdateBlips()

#pragma region Peds
	// ------------------- Peds -------------
	
	GTA::Ped^ World::CreatePed(GTA::eModel model,Vector3^ pos)
	{
		GTA::Ped^ newped;
		Scripting::eModel scrmodel = (Scripting::eModel)model;
		try
		{
			Scripting::RequestModel(scrmodel);
			for(int i=0;i<=1000;i++)
			{
				if (Scripting::HasModelLoaded(scrmodel)) break;
				Scripting::RequestModel(scrmodel);
			}
			if (!Scripting::HasModelLoaded(scrmodel)) 
			{
				GTA::Log::Warning("Unable to load ped model!");
				Scripting::MarkModelAsNoLongerNeeded(scrmodel);
				return nullptr;
			}
			newped = gcnew GTA::Ped();
			Scripting::Ped p;
			pin_ptr<Scripting::Ped> ppp = &p;
			Scripting::CreateChar(0,scrmodel,pos->X,pos->Y,pos->Z,ppp,true);
			newped = gcnew GTA::Ped(p);
			Scripting::MarkModelAsNoLongerNeeded(scrmodel);
		}
		catch (Exception^ ex)
		{
			GTA::Log::Exception(ex);
		}
		return newped;
	}

	

	// a very ugly hack to get all peds
	// just brute force DoesCharExist() function & the GetCharCooordinates() function
	// if both those functions succeed then we can assume its a valid ped to return
	// It runs on a tick & adds to peds collection, only scanning a small part each tick, to avoid freezes caused by a full scan
#pragma warning( disable : 4101 )
	void World::SearchPedsTick()
    {
		u32 maxid = 80*256; // 80 peds
		u32 maxtries = 5; // max id's to try each tick
		u32 counttofind = 1; // max peds to find each tick
		u32 found = 0; // peds found count this tick
		u32 lastfound = currentpedsearchid; // id of last ped found
		u32 pos = currentpedsearchid; // current search id
		for (u32 loop = 0;loop < maxtries;loop++)
		{
			if (found >= counttofind) break; // found enough already
			if (pos >= maxid) break; // beyond max id
			pos+=256; // go to next ped id
			if (!ScriptApi::DoesCharExist(gcnew Ped(pos))) pos = FindNextPed(lastfound+126); // make sure it is a ped id, if not then search for one from last
			lastfound = pos; // we have found one
			Ped^ ped = gcnew Ped(pos);
			try { Vector3^ coords = ped->Coordinates; } catch (Exception^ ex) { continue; } // try to get coords, if fails then just continue loop to next pedid
			Peds->Add(lastfound); // we have found a ped, so add it to our peds collection
			found++;
			//GTA::Log::Debug("SearchPedsTick found: "+lastfound + "  /  "+currentpedsearchid);
		}
		if (pos >= maxid) currentpedsearchid = 126; // we have gone beyond max id, so put it back to start for next tick
		else currentpedsearchid = pos; 
		
	}
#pragma warning( default : 4101 )
	u32 World::FindNextPed(u32 starthandle)
	{
		for (u32 i=starthandle;i<starthandle+513;i++)
		{
			if (ScriptApi::DoesCharExist(gcnew Ped(i))) return i;
		}
		return 0;
	}

	
	array<Ped^>^ World::GetAllPeds()
    {
		//GTA::Log::Debug("World GetAllPeds ");
		array<Ped^>^ retpeds = gcnew array<Ped^>(Peds->Count);
		u32 retpedspos = 0;
		for (u32 i=0;i<Peds->Length;i++)
		{
			if (retpedspos >= (u32)retpeds->Length) break;
			if (Peds->ObjectHandles[i] == 0) continue;
			bool isexcluded=false;
			for each (Ped^ ped in GetPedsExclusions)
			{ // check if obj is in exclusions list
				if (ped == nullptr) continue;
				if (!Ped::Exists(ped)) RemoveGetPedsExclusion(ped);
				if (ped->Handle == i) { isexcluded = true; break; }
			}
			if (isexcluded) continue;
			retpeds[retpedspos] = gcnew Ped(Peds->ObjectHandles[i]);
			retpedspos++;
		}
		return retpeds;
	}

#pragma warning( disable : 4101 )
	array<Ped^>^ World::GetAllPedsInArea(Vector3^ loc, float dist)
    {
		//GTA::Log::Debug("World GetAllPedsInArea ");
		System::Collections::Generic::List<Ped^>^ foundpeds = gcnew System::Collections::Generic::List<Ped^>();
		u32 count=0;
		for each (Ped^ p in GetAllPeds())
		{
			try
			{
				if (p == nullptr) continue;
				if (!p->Exists()) continue;
				float thisdist = 0.0F;
				
				try { Scripting::GetDistanceBetweenCoords3D(loc->X,loc->Y,loc->Z,p->Coordinates->X,p->Coordinates->Y,p->Coordinates->Z,&thisdist); } catch (Exception^ ex) { continue; }
				if (thisdist != thisdist) continue; // NaN comparison should return false. http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx
				if ((thisdist > dist) || (thisdist <= 0.0F)) continue;
				foundpeds->Add(p);
			}
			catch (Exception^ ex)
			{
				//GTA::Log::Debug("World GetAllPedsInArea exception overall: "+p->Handle);
				GTA::Log::Exception(ex);
				continue;
			}
		}
		array<Ped^>^ retpeds = gcnew array<Ped^>(foundpeds->Count);
		for (u32 i=0;i<(u32)foundpeds->Count;i++)
		{
			retpeds[i] = foundpeds[i];
		}
		return retpeds;
	}
#pragma warning( default : 4101 )

#pragma warning( disable : 4101 )
	Ped^ World::GetClosestPedInArea(Vector3^ loc, float area)
	{
		Ped^ closest;
		float closestdist = 1000.0F;
		for each (Ped^ ped in GetAllPedsInArea(loc,area))
		{
			if (ped == nullptr) continue;
			if (!ped->Exists()) continue;
			float thisdist = 0.0F;
			try { Scripting::GetDistanceBetweenCoords3D(loc->X,loc->Y,loc->Z,ped->Coordinates->X,ped->Coordinates->Y,ped->Coordinates->Z,&thisdist); } catch (Exception^ ex) { continue; }
			if (thisdist != thisdist) continue; // NaN comparison should return false. http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx
			if (thisdist <= 0.0F) continue;
			if (closest == nullptr) {closest = ped; closestdist = thisdist; continue;}
			if (thisdist > closestdist) continue;
			closest = ped;
			closestdist = thisdist;
		}

		return closest;
	}
#pragma warning( default : 4101 )


	void World::AddGetPedsExclusion(GTA::Ped^ ped)
	{
		if (!Ped::Exists(ped)) return;
		if (GetPedsExclusions->Contains(ped)) return;
		GetPedsExclusions->Add(ped);
	}

	void World::RemoveGetPedsExclusion(GTA::Ped^ ped)
	{
		if (ped == nullptr) return;
		if (!GetPedsExclusions->Contains(ped)) return;
		GetPedsExclusions->Remove(ped);
	}

	// -------------------------
#pragma endregion CreatePed(), SearchPedsTick(), FindNextPed(), GetAllPedsInArea(), GetClosestPedInArea()

#pragma region Vehicle
	// ------------- Vehicle ------------
	
	GTA::Vehicle^ World::CreateVehicle(GTA::eModel model,Vector3^ pos)
	{
		GTA::Vehicle^ newcar;
		Scripting::eModel scrmodel = (Scripting::eModel)model;

		try
		{
			Scripting::RequestModel(scrmodel);
			for(int i=0;i<=1000;i++)
			{
				if (Scripting::HasModelLoaded(scrmodel)) break;
				Scripting::RequestModel(scrmodel);
			}
			if (!Scripting::HasModelLoaded(scrmodel)) 
			{
				GTA::Log::Warning("Unable to load vehicle model!");
				Scripting::MarkModelAsNoLongerNeeded(scrmodel);
				return nullptr;
			}
			Scripting::Vehicle v;
			pin_ptr<Scripting::Vehicle> ppv = &v;
			Scripting::CreateCar((u32)model,pos->X,pos->Y,pos->Z,ppv,true);
			newcar = gcnew GTA::Vehicle(v);
			Scripting::MarkModelAsNoLongerNeeded(scrmodel);
		}
		catch (Exception^ ex)
		{
			GTA::Log::Exception(ex);
		}
		return newcar;
	}


	// a very ugly hack to get all vehicles
	// just brute force DoesCarExist() function & the GetCarCooordinates() function
	// if both those functions succeed then we can assume its a valid vehicle to return
	// It runs on a tick & adds to vehicles collection, only scanning a small part each tick, to avoid freezes caused by a full scan
#pragma warning( disable : 4101 )
	void World::SearchVehiclesTick()
    {
		u32 maxid = 80*256; // 80 vehicles
		u32 maxtries = 5; // max id's to try each tick
		u32 counttofind = 1; // max vehicles to find each tick
		u32 found = 0; // vehicles found count this tick
		u32 lastfound = currentvehiclesearchid; // id of last vehicle found
		u32 pos = currentvehiclesearchid; // current search id
		for (u32 loop = 0;loop < maxtries;loop++)
		{
			if (found >= counttofind) break; // found enough already
			if (pos >= maxid) break; // beyond max id
			pos+=256; // go to next vehicle id
			if (!ScriptApi::DoesVehicleExist(gcnew Vehicle(pos))) pos = FindNextVehicle(lastfound+126); // make sure it is a vehicle id, if not then search for one from last
			lastfound = pos; // we have found one
			Vehicle^ vehicle = gcnew Vehicle(pos);
			try { Vector3^ coords = vehicle->Coordinates; } catch (Exception^ ex) { continue; } // try to get coords, if fails then just continue loop to next vehicleid
			Vehicles->Add(lastfound); // we have found a vehicle, so add it to our vehicles collection
			found++;
			//GTA::Log::Debug("SearchVehiclesTick found: "+lastfound + "  /  "+currentvehiclesearchid);
		}
		if (pos >= maxid) currentvehiclesearchid = 126; // we have gone beyond max id, so put it back to start for next tick
		else currentvehiclesearchid = pos; 
		
	}
#pragma warning( default : 4101 )

	u32 World::FindNextVehicle(u32 starthandle)
	{
		for (u32 i=starthandle;i<starthandle+513;i++)
		{
			if (ScriptApi::DoesVehicleExist(gcnew Vehicle(i))) return i;
		}
		return 0;
	}

	
	array<Vehicle^>^ World::GetAllVehicles()
    {
		//GTA::Log::Debug("World GetAllVehicles ");
		array<Vehicle^>^ retvehicles = gcnew array<Vehicle^>(Vehicles->Count);
		u32 retvehiclespos = 0;
		for (u32 i=0;i<Vehicles->Length;i++)
		{
			if (retvehiclespos >= (u32)retvehicles->Length) break;
			if (Vehicles->ObjectHandles[i] == 0) continue;
			bool isexcluded=false;
			for each (Vehicle^ veh in GetVehiclesExclusions)
			{ // check if obj is in exclusions list
				if (veh == nullptr) continue;
				if (!Vehicle::Exists(veh)) RemoveGetVehiclesExclusion(veh);
				if (veh->Handle == i) { isexcluded = true; break; }
			}
			if (isexcluded) continue;
			retvehicles[retvehiclespos] = gcnew Vehicle(Vehicles->ObjectHandles[i]);
			retvehiclespos++;
		}
		return retvehicles;
	}

#pragma warning( disable : 4101 )	
	array<Vehicle^>^ World::GetAllVehiclesInArea(Vector3^ loc, float dist)
    {
		//GTA::Log::Debug("World GetAllVehiclesInArea ");
		System::Collections::Generic::List<Vehicle^>^ foundvehicles = gcnew System::Collections::Generic::List<Vehicle^>();
		u32 count=0;
		for each (Vehicle^ p in GetAllVehicles())
		{
			try
			{
				if (p == nullptr) continue;
				if (!p->Exists()) continue;
				float thisdist = 0.0F;
				
				try { Scripting::GetDistanceBetweenCoords3D(loc->X,loc->Y,loc->Z,p->Coordinates->X,p->Coordinates->Y,p->Coordinates->Z,&thisdist); } catch (Exception^ ex) { continue; }
				if (thisdist != thisdist) continue; // NaN comparison should return false. http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx
				if ((thisdist > dist) || (thisdist <= 0.0F)) continue;
				foundvehicles->Add(p);
			}
			catch (Exception^ ex)
			{
				//GTA::Log::Debug("World GetAllVehiclesInArea exception overall: "+p->Handle);
				GTA::Log::Exception(ex);
				continue;
			}
		}
		array<Vehicle^>^ retvehicles = gcnew array<Vehicle^>(foundvehicles->Count);
		for (u32 i=0;i<(u32)foundvehicles->Count;i++)
		{
			retvehicles[i] = foundvehicles[i];
		}
		return retvehicles;
	}
#pragma warning( default : 4101 )

#pragma warning( disable : 4101 )
	Vehicle^ World::GetClosestVehicleInArea(Vector3^ loc, float area)
	{
		Vehicle^ closest;
		float closestdist = 1000.0F;
		for each (Vehicle^ vehicle in GetAllVehiclesInArea(loc,area))
		{
			if (vehicle == nullptr) continue;
			if (!vehicle->Exists()) continue;
			float thisdist = 0.0F;
			try { Scripting::GetDistanceBetweenCoords3D(loc->X,loc->Y,loc->Z,vehicle->Coordinates->X,vehicle->Coordinates->Y,vehicle->Coordinates->Z,&thisdist); } catch (Exception^ ex) { continue; }
			if (thisdist != thisdist) continue; // NaN comparison should return false. http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx
			if (thisdist <= 0.0F) continue;
			if (closest == nullptr) {closest = vehicle; closestdist = thisdist; continue;}
			if (thisdist > closestdist) continue;
			closest = vehicle;
			closestdist = thisdist;
		}

		return closest;
	}
#pragma warning( default : 4101 )

	void World::AddGetVehiclesExclusion(GTA::Vehicle^ veh)
	{
		if (!Vehicle::Exists(veh)) return;
		if (GetVehiclesExclusions->Contains(veh)) return;
		GetVehiclesExclusions->Add(veh);
	}

	void World::RemoveGetVehiclesExclusion(GTA::Vehicle^ veh)
	{
		if (veh == nullptr) return;
		if (!GetVehiclesExclusions->Contains(veh)) return;
		GetVehiclesExclusions->Remove(veh);
	}

	// ---------------------------------
#pragma endregion CreateVehicle(), SearchVehiclesTick(), FindNextVehicle(), GetAllVehiclesInArea(), GetClosestVehicleInArea()

#pragma region GameObject
	// ------------- GameObject -----------

	GTA::GameObject^ World::CreateObject(GTA::eModel model,Vector3^ pos)
	{
		GTA::GameObject^ newobj;
		Scripting::eModel scrmodel = (Scripting::eModel)model;
		try
		{
			Scripting::RequestModel(scrmodel);
			for(int i=0;i<=1000;i++)
			{
				if (Scripting::HasModelLoaded(scrmodel)) break;
				Scripting::RequestModel(scrmodel);
			}
			if (!Scripting::HasModelLoaded(scrmodel)) 
			{
				GTA::Log::Warning("Unable to load obj model!");
				Scripting::MarkModelAsNoLongerNeeded(scrmodel);
				return nullptr;
			}
			newobj = gcnew GTA::GameObject();
			Scripting::Object o;
			pin_ptr<Scripting::Object> ppo = &o;
			Scripting::CreateObject(scrmodel,pos->X,pos->Y,pos->Z,ppo,true);
			newobj = gcnew GTA::GameObject(o);
			Scripting::MarkModelAsNoLongerNeeded(scrmodel);
		}
		catch (Exception^ ex)
		{
			GTA::Log::Exception(ex);
		}
		return newobj;
	}



	// a very ugly hack to get all gameobjects
	// just brute force DoesCarExist() function & the GetCarCooordinates() function
	// if both those functions succeed then we can assume its a valid gameobject to return
	// It runs on a tick & adds to gameobjects collection, only scanning a small part each tick, to avoid freezes caused by a full scan
#pragma warning( disable : 4101 )
	void World::SearchObjectsTick()
    {
		u32 maxid = 800*256; // 800 gameobjects
		u32 maxtries = 10; // max id's to try each tick
		u32 counttofind = 3; // max gameobjects to find each tick
		u32 found = 0; // gameobjects found count this tick
		u32 lastfound = currentobjectsearchid; // id of last gameobject found
		u32 pos = currentobjectsearchid; // current search id
		for (u32 loop = 0;loop < maxtries;loop++)
		{
			if (found >= counttofind) break; // found enough already
			if (pos >= maxid) break; // beyond max id
			pos+=256; // go to next gameobject id
			if (!ScriptApi::DoesObjectExist(gcnew GameObject(pos))) pos = FindNextObject(lastfound+126); // make sure it is a gameobject id, if not then search for one from last
			lastfound = pos; // we have found one
			GameObject^ gameobject = gcnew GameObject(pos);
			try { Vector3^ coords = gameobject->Coordinates; } catch (Exception^ ex) { continue; } // try to get coords, if fails then just continue loop to next gameobjectid
			Objects->Add(lastfound); // we have found a gameobject, so add it to our gameobjects collection
			found++;
			//GTA::Log::Debug("SearchGameObjectsTick found: "+lastfound + "  /  "+currentgameobjectsearchid);
		}
		if (pos >= maxid) currentobjectsearchid = 126; // we have gone beyond max id, so put it back to start for next tick
		else currentobjectsearchid = pos; 
		
	}
#pragma warning( default : 4101 )

	u32 World::FindNextObject(u32 starthandle)
	{
		for (u32 i=starthandle;i<starthandle+513;i++)
		{
			if (ScriptApi::DoesObjectExist(gcnew GameObject(i))) return i;
		}
		return 0;
	}

	
	array<GameObject^>^ World::GetAllObjects()
    {
		//GTA::Log::Debug("World GetAllGameObjects ");
		array<GameObject^>^ retgameobjects = gcnew array<GameObject^>(Objects->Count);
		u32 retgameobjectspos = 0;
		for (u32 i=0;i<Objects->Length;i++)
		{
			if (retgameobjectspos >= (u32)retgameobjects->Length) break;
			if (Objects->ObjectHandles[i] == 0) continue;
			bool isexcluded=false;
			for each (GameObject^ obj in GetObjectsExclusions)
			{ // check if obj is in exclusions list
				if (obj == nullptr) continue;
				if (!GameObject::Exists(obj)) RemoveGetObjectsExclusion(obj);


			if (obj->Handle == i)
			{
				GTA::eModel debugmod = GTA::eModel::MODEL_ADMIRAL;
				GTA::ScriptApi::GetObjectModel(obj,&debugmod);
				GTA::Log::Debug("Object exclusion found: "+debugmod.ToString());
			}


				if (obj->Handle == i) { isexcluded = true; break; }
			}
			


			if (isexcluded) continue;
			retgameobjects[retgameobjectspos] = gcnew GameObject(Objects->ObjectHandles[i]);
			retgameobjectspos++;
		}
		return retgameobjects;
	}

#pragma warning( disable : 4101 )
	array<GameObject^>^ World::GetAllObjectsInArea(Vector3^ loc, float dist)
    {
		//GTA::Log::Debug("World GetAllGameObjectsInArea ");
		System::Collections::Generic::List<GameObject^>^ foundgameobjects = gcnew System::Collections::Generic::List<GameObject^>();
		u32 count=0;
		for each (GameObject^ p in GetAllObjects())
		{
			try
			{
				if (p == nullptr) continue;
				if (!p->Exists()) continue;
				float thisdist = 0.0F;
				try { Scripting::GetDistanceBetweenCoords3D(loc->X,loc->Y,loc->Z,p->Coordinates->X,p->Coordinates->Y,p->Coordinates->Z,&thisdist); } catch (Exception^ ex) { continue; }
				if (thisdist != thisdist) continue; // NaN comparison should return false. http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx
				if ((thisdist > dist) || (thisdist <= 0.0F)) continue;
				foundgameobjects->Add(p);
			}
			catch (Exception^ ex)
			{
				//GTA::Log::Debug("World GetAllGameObjectsInArea exception overall: "+p->Handle);
				GTA::Log::Exception(ex);
				continue;
			}
		}
		array<GameObject^>^ retgameobjects = gcnew array<GameObject^>(foundgameobjects->Count);
		for (u32 i=0;i<(u32)foundgameobjects->Count;i++)
		{
			retgameobjects[i] = foundgameobjects[i];
		}
		return retgameobjects;
	}
#pragma warning( default : 4101 )

#pragma warning( disable : 4101 )
	GameObject^ World::GetClosestObjectInArea(Vector3^ loc, float area)
	{
		GameObject^ closest;
		float closestdist = 1000.0F;
		for each (GameObject^ gameobject in GetAllObjectsInArea(loc,area))
		{
			if (gameobject == nullptr) continue;
			if (!gameobject->Exists()) continue;
			float thisdist = 0.0F;
			try { Scripting::GetDistanceBetweenCoords3D(loc->X,loc->Y,loc->Z,gameobject->Coordinates->X,gameobject->Coordinates->Y,gameobject->Coordinates->Z,&thisdist); } catch (Exception^ ex) { continue; }
			if (thisdist != thisdist) continue; // NaN comparison should return false. http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx
			if (thisdist <= 0.0F) continue;
			if (closest == nullptr) {closest = gameobject; closestdist = thisdist; continue;}
			if (thisdist > closestdist) continue;
			closest = gameobject;
			closestdist = thisdist;
		}

		return closest;
	}
#pragma warning( default : 4101 )

	void World::AddGetObjectsExclusion(GTA::GameObject^ obj)
	{
		if (!GameObject::Exists(obj)) return;
		if (GetObjectsExclusions->Contains(obj)) return;
		GetObjectsExclusions->Add(obj);
	}

	void World::RemoveGetObjectsExclusion(GTA::GameObject^ obj)
	{
		if (obj == nullptr) return;
		if (!GetObjectsExclusions->Contains(obj)) return;
		GetObjectsExclusions->Remove(obj);
	}

	// ------------------------------------------
#pragma endregion CreateObject(), SearchObjectsTick(), FindNextObject(), GetAllObjectsInArea(), GetClosestObjectInArea(), AddGetObjectsExclusion(), RemoveGetObjectsExclusion()

#pragma region PlayerAim

	void World::AttachAim()
	{ // initial attach, auto called by AimTick
		Player^ pl = gcnew Player();
		Vector3^ pos = pl->Coordinates;
		ScriptApi::RequestModel(eModel::MODEL_CJ_ARROW);
		ScriptApi::RequestModel(eModel::MODEL_CJ_BARRIER_1);
		CamObject = gcnew GameObject();
		AimObject = gcnew GameObject();
		ScriptApi::CreateObject(eModel::MODEL_CJ_ARROW, pos->X, pos->Y, pos->Z, CamObject, true);
		ScriptApi::CreateObject(eModel::MODEL_CJ_BARRIER_1, pos->X, pos->Y, pos->Z, AimObject, true);
		if (!GameObject::Exists(CamObject)) { DetachAim(); return; }
		if (!GameObject::Exists(AimObject)) { DetachAim(); return; }

		AddGetObjectsExclusion(CamObject); // stop object from being returned by World::GetAllObjects (so that scripts cant fuck with it)
		AddGetObjectsExclusion(AimObject);

		ScriptApi::SetObjectVisible(CamObject, false);
		ScriptApi::SetObjectCollision(CamObject, false);
		ScriptApi::SetObjectCollision(AimObject, false);
		ScriptApi::AttachObjectToObject(AimObject, CamObject, 0, 0.0F, 70.0F, 0.0F, 0.0F, 0.0F, 0.0F);

		ScriptApi::MarkModelAsNoLongerNeeded(eModel::MODEL_CJ_ARROW);
		ScriptApi::MarkModelAsNoLongerNeeded(eModel::MODEL_CJ_BARRIER_1);
	}

	void World::DetachAim()
	{
		RemoveGetObjectsExclusion(CamObject);
		RemoveGetObjectsExclusion(AimObject);
		if (GameObject::Exists(CamObject)) { ScriptApi::DeleteObject(CamObject); CamObject = nullptr;}
		if (GameObject::Exists(AimObject)) { ScriptApi::DeleteObject(AimObject); AimObject = nullptr; }
	}

	void World::AimTick()
	{
		if (!GameObject::Exists(CamObject)) { AttachAim(); return; }
		if (!GameObject::Exists(AimObject)) { AttachAim(); return; }

		Camera^ cam = gcnew Camera();
		ScriptApi::GetGameCam(cam);
		float camposX, camposY, camposZ;
		ScriptApi::GetCamPos(cam, &camposX, &camposY, &camposZ);
		float camdirX, camdirY, camdirZ;
		ScriptApi::GetCamRot(cam, &camdirX, &camdirY, &camdirZ);

		CamObject->Coordinates = gcnew Vector3(camposX,camposY,camposZ);
		ScriptApi::SetObjectRotation(CamObject, camdirX, camdirY, camdirZ);
	}

	Vector3^ World::GetAimCoordinates(float distance)
	{
		if (!GameObject::Exists(CamObject)) { AttachAim(); return nullptr; }
		if (!GameObject::Exists(AimObject)) { AttachAim(); return nullptr; }
		ScriptApi::AttachObjectToObject(AimObject, CamObject, 0, 0.0F, distance, 0.0F, 0.0F, 0.0F, 0.0F);
		GTA::Log::Debug("Returning aim coords");
		return AimObject->Coordinates;
	}


#pragma endregion
};